/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import java.util.ArrayList;
import java.util.Arrays;

class ConstNumber {
    public static final int ARRAY_INDEX2 = 2;
    public static final int ARRAY_INDEX3 = 3;
    public static final int ARRAY_INDEX4 = 4;
    public static final int ARRAY_INDEX5 = 5;
    public static final int ARRAY_INDEX6 = 6;
    public static final int ARRAY_INDEX7 = 7;
    public static final int ARRAY_INDEX8 = 8;
    public static final int ARRAY_INDEX9 = 9;
    public static final int ARRAY_INDEX10 = 10;
    public static final int ARRAY_INDEX11 = 11;
    public static final int ARRAY_INDEX12 = 12;
    public static final int ARRAY_INDEX13 = 13;

    public static final int ARRAY_LENGTH16 = 16;
    public static final int ARRAY_LENGTH4 = 4;

    public static final int ITERATION_COUNT3 = 3;
    public static final int ITERATION_COUNT4 = 4;
    public static final int ITERATION_COUNT11 = 11;

    public static final int NORMAL_NUMBER2 = 2;
    public static final int NORMAL_NUMBER3 = 3;
    public static final int NORMAL_NUMBER4 = 4;
    public static final int NORMAL_NUMBER10 = 10;
    public static final int NORMAL_NUMBER17 = 17;
    public static final int NORMAL_NUMBER32 = 32;
    public static final int NORMAL_NUMBER40 = 40;
    public static final int NORMAL_NUMBER1000 = 1000;
    public static final int NORMAL_NUMBER30 = 30;
    public static final int NORMAL_NUMBER20 = 20;
    public static final int NORMAL_NUMBER38 = 38;
    public static final int NORMAL_NUMBER22 = 22;
    public static final int NORMAL_NUMBER23 = 23;

    public static final double NORMAL_FLOAT_0P1 = 0.1;
    public static final double NORMAL_FLOAT_0P4 = 0.4;
    public static final double NORMAL_FLOAT_0P3 = 0.3;
    public static final double NORMAL_FLOAT_0P7 = 0.7;

    public static final double VECTOR_INITVAL_P7 = 0.7;
    public static final double VECTOR_INITVAL_P8 = 0.8;

    public static final double COLOUR_REFLECTION_DOWNLINE = 0.001;
    public static final double COLOUR_INSECTION_SMALLVAL = 0.0001;
    public static final double COLOUR_INSECTION_BIGVAL = 1000000;
    public static final double COLOUR_REFLECTION_DOWNLINE999 = 0.999999;
    public static final double DISTANCE_SMALL_VAL = 0.0001;
    public static final int ITERATION_RUNCOUNT = 80;
}

/// 矢量类 存储x,y,z三个浮点数，用来表示一个位置，一个像素点
class Vector {
    public double[] values;
    public Double reflection;
    public Vector colour;

    public static Vector zero() {
        return new Vector(0, 0, 0);
    }

    Vector(double x, double y, double z) {
        values = new double[]{x, y, z};
    }

    // log打印使用
    public String description() {
        return "[" + scriptString() + "]";
    }

    // 绘制脚本拼接使用
    public String scriptString() {
        return values[0] + "," + values[1] + "," + values[ConstNumber.ARRAY_INDEX2];
    }
}

class GlobalFunc1 {
    public static Vector createVector(double x, double y, double z) {
        return new Vector(x, y, z);
    }

    public static double sqrLengthVector(Vector vector) {
        return vector.values[0] * vector.values[0] + vector.values[1] * vector.values[1] + vector.values[ConstNumber.ARRAY_INDEX2] * vector.values[ConstNumber.ARRAY_INDEX2];
    }

    public static double lengthVector(Vector vector) {
        return Math.sqrt(vector.values[0] * vector.values[0] + vector.values[1] * vector.values[1] + vector.values[ConstNumber.ARRAY_INDEX2] * vector.values[ConstNumber.ARRAY_INDEX2]);
    }

    public static Vector addVector(Vector vector, Vector v) {
        vector.values[0] += v.values[0];
        vector.values[1] += v.values[1];
        vector.values[ConstNumber.ARRAY_INDEX2] += v.values[ConstNumber.ARRAY_INDEX2];
        return vector;
    }

    public static Vector subVector(Vector vector, Vector v) {
        vector.values[0] -= v.values[0];
        vector.values[1] -= v.values[1];
        vector.values[ConstNumber.ARRAY_INDEX2] -= v.values[ConstNumber.ARRAY_INDEX2];
        return vector;
    }

    public static Vector scaleVector(Vector vector, double scale) {
        vector.values[0] *= scale;
        vector.values[1] *= scale;
        vector.values[ConstNumber.ARRAY_INDEX2] *= scale;
        return vector;
    }

    public static Vector normaliseVector(Vector vector) {
        double len = Math.sqrt(vector.values[0] * vector.values[0] + vector.values[1] * vector.values[1] + vector.values[ConstNumber.ARRAY_INDEX2] * vector.values[ConstNumber.ARRAY_INDEX2]);
        vector.values[0] /= len;
        vector.values[1] /= len;
        vector.values[ConstNumber.ARRAY_INDEX2] /= len;
        return vector;
    }

    public static Vector add(Vector v1, Vector v2) {
        return new Vector(v1.values[0] + v2.values[0], v1.values[1] + v2.values[1], v1.values[ConstNumber.ARRAY_INDEX2] + v2.values[ConstNumber.ARRAY_INDEX2]);
    }

    public static Vector sub(Vector v1, Vector v2) {
        return new Vector(v1.values[0] - v2.values[0], v1.values[1] - v2.values[1], v1.values[ConstNumber.ARRAY_INDEX2] - v2.values[ConstNumber.ARRAY_INDEX2]);
    }

    public static Vector scalev(Vector v1, Vector v2) {
        return new Vector(v1.values[0] * v2.values[0], v1.values[1] * v2.values[1], v1.values[ConstNumber.ARRAY_INDEX2] * v2.values[ConstNumber.ARRAY_INDEX2]);
    }

    public static double dot(Vector v1, Vector v2) {
        return v1.values[0] * v2.values[0] + v1.values[1] * v2.values[1] + v1.values[ConstNumber.ARRAY_INDEX2] * v2.values[ConstNumber.ARRAY_INDEX2];
    }

    public static Vector scale(Vector v, double scale) {
        return new Vector(v.values[0] * scale, v.values[1] * scale, v.values[ConstNumber.ARRAY_INDEX2] * scale);
    }

    public static Vector cross(Vector v1, Vector v2) {
        return new Vector(
                v1.values[1] * v2.values[ConstNumber.ARRAY_INDEX2] - v1.values[ConstNumber.ARRAY_INDEX2] * v2.values[1],
                v1.values[ConstNumber.ARRAY_INDEX2] * v2.values[0] - v1.values[0] * v2.values[ConstNumber.ARRAY_INDEX2],
                v1.values[0] * v2.values[1] - v1.values[1] * v2.values[0]
        );
    }

    public static Vector normalise(Vector v) {
        double len = lengthVector(v);
        return new Vector(v.values[0] / len, v.values[1] / len, v.values[ConstNumber.ARRAY_INDEX2] / len);
    }

    public static Vector transformMatrix(double[] matrix, Vector v) {
        double[] vals = matrix;
        double x = vals[0] * v.values[0] + vals[1] * v.values[1] + vals[ConstNumber.ARRAY_INDEX2] * v.values[ConstNumber.ARRAY_INDEX2] + vals[ConstNumber.ARRAY_INDEX3];
        double y = vals[ConstNumber.ARRAY_INDEX4] * v.values[0] + vals[ConstNumber.ARRAY_INDEX5] * v.values[1] + vals[ConstNumber.ARRAY_INDEX6] * v.values[ConstNumber.ARRAY_INDEX2] + vals[ConstNumber.ARRAY_INDEX7];
        double z = vals[ConstNumber.ARRAY_INDEX8] * v.values[0] + vals[ConstNumber.ARRAY_INDEX9] * v.values[1] + vals[ConstNumber.ARRAY_INDEX10] * v.values[ConstNumber.ARRAY_INDEX2] + vals[ConstNumber.ARRAY_INDEX11];
        return new Vector(x, y, z);
    }

    public static double[] invertMatrix(double[] matrix) {
        double[] temp = new double[ConstNumber.ARRAY_LENGTH16];
        double tx = -matrix[ConstNumber.ARRAY_INDEX3];
        double ty = -matrix[ConstNumber.ARRAY_INDEX7];
        double tz = -matrix[ConstNumber.ARRAY_INDEX11];
        for (int h = 0; h < ConstNumber.ITERATION_COUNT3; h++) {
            for (int v = 0; v < ConstNumber.ITERATION_COUNT3; v++) {
                temp[h + v * ConstNumber.NORMAL_NUMBER4] = matrix[v + h * ConstNumber.NORMAL_NUMBER4];
            }
        }
        for (int i = 0; i < ConstNumber.ITERATION_COUNT11; i++) {
            matrix[i] = temp[i];
        }
        matrix[ConstNumber.ARRAY_INDEX3] = tx * matrix[0] + ty * matrix[1] + tz * matrix[ConstNumber.ARRAY_INDEX2];
        matrix[ConstNumber.ARRAY_INDEX7] = tx * matrix[ConstNumber.ARRAY_INDEX4] + ty * matrix[ConstNumber.ARRAY_INDEX5] + tz * matrix[ConstNumber.ARRAY_INDEX6];
        matrix[ConstNumber.ARRAY_INDEX11] = tx * matrix[ConstNumber.ARRAY_INDEX8] + ty * matrix[ConstNumber.ARRAY_INDEX9] + tz * matrix[ConstNumber.ARRAY_INDEX10];
        return matrix;
    }
}

@FunctionalInterface
interface ShaderCallback {
    Vector shader(Triangle triangle, Vector v1, Vector v2);
}
//三角形
class Triangle {
    public int axis;
    public Vector normal;
    public double nu;
    public double nv;
    public double nd;
    public double eu;
    public double ev;
    public double nu1;
    public double nv1;
    public double nu2;
    public double nv2;
    public Vector material;
    public ShaderCallback shader;

    public Triangle(Vector p1, Vector p2, Vector p3) {
        Vector edge1 = GlobalFunc1.sub(p3, p1);
        Vector edge2 = GlobalFunc1.sub(p2, p1);
        Vector normal = GlobalFunc1.cross(edge1, edge2);
        if (Math.abs(normal.values[0]) > Math.abs(normal.values[1])) {
            if (Math.abs(normal.values[0]) > Math.abs(normal.values[ConstNumber.ARRAY_INDEX2])) {
                this.axis = 0;
            } else {
                this.axis = 2;
            }
        } else if (Math.abs(normal.values[1]) > Math.abs(normal.values[ConstNumber.ARRAY_INDEX2])) {
            this.axis = 1;
        } else {
            this.axis = 2;
        }
        int u = (this.axis + 1) % ConstNumber.NORMAL_NUMBER3;
        int v = (this.axis + ConstNumber.NORMAL_NUMBER2) % ConstNumber.NORMAL_NUMBER3;
        double u1 = edge1.values[u];
        double v1 = edge1.values[v];
        double u2 = edge2.values[u];
        double v2 = edge2.values[v];
        this.normal = GlobalFunc1.normalise(normal);
        this.nu = normal.values[u] / normal.values[this.axis];
        this.nv = normal.values[v] / normal.values[this.axis];
        this.nd = GlobalFunc1.dot(normal, p1) / normal.values[this.axis];
        double det = u1 * v2 - v1 * u2;
        this.eu = p1.values[u];
        this.ev = p1.values[v];
        this.nu1 = u1 / det;
        this.nv1 = -v1 / det;
        this.nu2 = v2 / det;
        this.nv2 = -u2 / det;
        this.material = new Vector(ConstNumber.VECTOR_INITVAL_P7, ConstNumber.VECTOR_INITVAL_P7, ConstNumber.VECTOR_INITVAL_P7);
    }

    public Double intersect(Vector orig, Vector dir, Double near, Double far) {
        int u = (this.axis + 1) % ConstNumber.NORMAL_NUMBER3;
        int v = (this.axis + ConstNumber.NORMAL_NUMBER2) % ConstNumber.NORMAL_NUMBER3;
        double d = dir.values[this.axis] + this.nu * dir.values[u] + this.nv * dir.values[v];
        double t = (this.nd - orig.values[this.axis] - this.nu * orig.values[u] - this.nv * orig.values[v]) / d;
        if (near != null && far != null) {
            if (t < near || t > far) {
                return null;
            }
        }
        double pu = orig.values[u] + t * dir.values[u] - this.eu;
        double pv = orig.values[v] + t * dir.values[v] - this.ev;
        double a2 = pv * this.nu1 + pu * this.nv1;
        if (a2 < 0) {
            return null;
        }
        double a3 = pu * this.nu2 + pv * this.nv2;
        if (a3 < 0) {
            return null;
        }
        if ((a2 + a3) > 1) {
            return null;
        }
        return t;
    }
}

// 场景
class Scene {
    public ArrayList<Triangle> triangles;
    public Vector[] lights;
    public Vector ambient;
    public Vector background;

    public Scene(ArrayList<Triangle> aTriangles) {
        this.triangles = aTriangles;
        this.lights = new Vector[]{Vector.zero(), Vector.zero(), Vector.zero()};
        this.ambient = Vector.zero();
        this.background = new Vector(ConstNumber.VECTOR_INITVAL_P8, ConstNumber.VECTOR_INITVAL_P8,1);
    }

    public void lightBlocked(Vector hit, Vector normal, Vector l) {
        for (int i = 0; i < this.lights.length; i++) {
            Vector light = this.lights[i];
            Vector toLight = GlobalFunc1.sub(light, hit);
            double distance = GlobalFunc1.lengthVector(toLight);
            GlobalFunc1.scaleVector(toLight, 1.0/distance);
            distance -= ConstNumber.DISTANCE_SMALL_VAL;
            if (this.blocked(hit, toLight, distance)) {
                continue;
            }
            double nl = GlobalFunc1.dot(normal, toLight);
            if (nl > 0) {
                GlobalFunc1.addVector(l, GlobalFunc1.scale(light.colour, nl));
            }
        }
    }

    public Vector intersect(Vector origin, Vector dir, Double near, Double far) {
        Triangle closest = null;
        Double rfar = far;
        for (int i = 0; i < this.triangles.size(); i++) {
            Triangle triangle = this.triangles.get(i);
            Double d = triangle.intersect(origin, dir, near, rfar);
            if (d == null) {
                continue;
            }
            rfar = d;
            closest = triangle;
        }

        if (closest == null || rfar == null) {
            return new Vector(this.background.values[0],this.background.values[1],this.background.values[ConstNumber.ARRAY_INDEX2]);
        }
        Vector normal = closest.normal;
        Vector hit = GlobalFunc1.add(origin, GlobalFunc1.scale(dir, rfar));
        if (GlobalFunc1.dot(dir, normal) > 0) {
            normal = new Vector(-normal.values[0], -normal.values[1], -normal.values[ConstNumber.ARRAY_INDEX2]);
        }
        Vector colour = null;
        if (closest.shader != null) {
            colour = closest.shader.shader(closest, hit, dir);
        } else {
            colour = closest.material;
        }

        // do reflection
        Vector reflected = null;
        if (colour.reflection != null) {
            if (colour.reflection > ConstNumber.COLOUR_REFLECTION_DOWNLINE) {
                Vector reflection = GlobalFunc1.addVector(GlobalFunc1.scale(normal, -ConstNumber.NORMAL_NUMBER2 * GlobalFunc1.dot(dir, normal)), dir);
                reflected = this.intersect(hit, reflection, ConstNumber.COLOUR_INSECTION_SMALLVAL, ConstNumber.COLOUR_INSECTION_BIGVAL);
                if (colour.reflection >= ConstNumber.COLOUR_REFLECTION_DOWNLINE999) {
                    return reflected;
                }
            }
        }

        Vector l = new Vector(this.ambient.values[0], this.ambient.values[1], this.ambient.values[ConstNumber.ARRAY_INDEX2]);
        lightBlocked(hit, normal, l);

        l = GlobalFunc1.scalev(l, colour);
        if (reflected != null) {
            l = GlobalFunc1.addVector(GlobalFunc1.scaleVector(l, 1 - colour.reflection), GlobalFunc1.scaleVector(reflected, colour.reflection));
        }
        return l;
    }

    public boolean blocked(Vector o, Vector d, double far) {
        double near = 0.0001;
        for (int i = 0; i < this.triangles.size(); i++) {
            Triangle triangle = this.triangles.get(i);
            Double d1 = triangle.intersect(o, d, near, far);
            if (d1 == null || d1 > far || d1 < near) {
                continue;
            }
            return true;
        }
        return false;
    }
}

// 光线
class Ray {
    public Vector origin;

    public Vector dir;

    Ray(Vector origin, Vector dir) {
        this.origin = origin;
        this.dir = dir;
    }

    public Ray zero() {
        return new Ray(Vector.zero(), Vector.zero());
    }
}

// 相机
class Camera {
    public Vector[] directions;
    public Vector origin;
    public Ray[] rays;

    Camera(Vector origin, Vector lookat, Vector up) {
        Vector zaxis = GlobalFunc1.normaliseVector(GlobalFunc1.subVector(lookat, origin));
        Vector xaxis = GlobalFunc1.normaliseVector(GlobalFunc1.cross(up, zaxis));
        Vector yaxis = GlobalFunc1.normaliseVector(GlobalFunc1.cross(xaxis, GlobalFunc1.subVector(Vector.zero(), zaxis)));
        double[] m = new double[ConstNumber.ARRAY_LENGTH16];
        m[0] = xaxis.values[0];
        m[1] = xaxis.values[1];
        m[ConstNumber.ARRAY_INDEX2] = xaxis.values[ConstNumber.ARRAY_INDEX2];
        m[ConstNumber.ARRAY_INDEX4] = yaxis.values[0];
        m[ConstNumber.ARRAY_INDEX5] = yaxis.values[1];
        m[ConstNumber.ARRAY_INDEX6] = yaxis.values[ConstNumber.ARRAY_INDEX2];
        m[ConstNumber.ARRAY_INDEX8] = zaxis.values[0];
        m[ConstNumber.ARRAY_INDEX9] = zaxis.values[1];
        m[ConstNumber.ARRAY_INDEX10] = zaxis.values[ConstNumber.ARRAY_INDEX2];
        GlobalFunc1.invertMatrix(m);
        m[ConstNumber.ARRAY_INDEX3] = 0;
        m[ConstNumber.ARRAY_INDEX7] = 0;
        m[ConstNumber.ARRAY_INDEX11] = 0;
        this.origin = origin;
        this.directions = new Vector[ConstNumber.ARRAY_LENGTH4];

        this.directions[0] = GlobalFunc1.normalise(new Vector(-ConstNumber.VECTOR_INITVAL_P7, ConstNumber.VECTOR_INITVAL_P7, 1));
        this.directions[1] = GlobalFunc1.normalise(new Vector(ConstNumber.VECTOR_INITVAL_P7, ConstNumber.VECTOR_INITVAL_P7, 1));
        this.directions[ConstNumber.ARRAY_INDEX2] = GlobalFunc1.normalise(new Vector(ConstNumber.VECTOR_INITVAL_P7, -ConstNumber.VECTOR_INITVAL_P7, 1));
        this.directions[ConstNumber.ARRAY_INDEX3] = GlobalFunc1.normalise(new Vector(-ConstNumber.VECTOR_INITVAL_P7, -ConstNumber.VECTOR_INITVAL_P7, 1));
        this.directions[0] = GlobalFunc1.transformMatrix(m, this.directions[0]);
        this.directions[1] = GlobalFunc1.transformMatrix(m, this.directions[1]);
        this.directions[ConstNumber.ARRAY_INDEX2] = GlobalFunc1.transformMatrix(m, this.directions[ConstNumber.ARRAY_INDEX2]);
        this.directions[ConstNumber.ARRAY_INDEX3] = GlobalFunc1.transformMatrix(m, this.directions[ConstNumber.ARRAY_INDEX3]);
        this.rays = new Ray[0];
    }

    /// 生成光线
    public Ray[] generateRayPair(double y) {
        Vector dir1 = GlobalFunc1.addVector(GlobalFunc1.scale(directions[0], y), GlobalFunc1.scale(directions[3], 1 - y));
        Ray ray1 = new Ray(origin, dir1);
        Vector dir2 = GlobalFunc1.addVector(GlobalFunc1.scale(directions[1], y), GlobalFunc1.scale(directions[ConstNumber.ARRAY_INDEX2], 1 - y));
        Ray ray2 = new Ray(origin, dir2);
        this.rays = new Ray[]{ray1, ray2};
        return this.rays;
    }

    public void render(Scene scene, Vector[][] pixels, int width, int height) {
        GlobalFunc2.renderRows(this, scene, pixels, width, height, 0, height);
    }
}

class GlobalFunc2 {
    public static void renderRows(Camera camera, Scene scene, Vector[][] pixels, int width, int height, int starty, int stopy) {
        int pointNum = 1;
        for (int y = starty; y < stopy; y++) {
            Ray[] rays = camera.generateRayPair((double) y / height);
            for (int x = 0; x < width; x++) {
                double xp = (double) x / width;
                Vector origin = GlobalFunc1.addVector(GlobalFunc1.scale(rays[0].origin, xp), GlobalFunc1.scale(rays[1].origin, 1 - xp));
                Vector dir = GlobalFunc1.normaliseVector(GlobalFunc1.addVector(GlobalFunc1.scale(rays[0].dir, xp), GlobalFunc1.scale(rays[1].dir, 1 - xp)));
                Vector l = scene.intersect(origin, dir, null, null);
                pixels[y][x] = l;
                if (Configuration.DEBUG) {
                    System.out.println("point " + pointNum + ": " + l.description());
                    pointNum += 1;
                }
            }
        }
    }

    /// 构建场景
    public static Vector[][] raytraceScene() {
        // 创建矢量，通过矢量构建三角形
        Vector tfl = GlobalFunc1.createVector(-ConstNumber.NORMAL_NUMBER10,  ConstNumber.NORMAL_NUMBER10, -ConstNumber.NORMAL_NUMBER10);
        Vector tfr = GlobalFunc1.createVector( ConstNumber.NORMAL_NUMBER10,  ConstNumber.NORMAL_NUMBER10, -ConstNumber.NORMAL_NUMBER10);
        Vector tbl = GlobalFunc1.createVector(-ConstNumber.NORMAL_NUMBER10,  ConstNumber.NORMAL_NUMBER10,  ConstNumber.NORMAL_NUMBER10);
        Vector tbr = GlobalFunc1.createVector( ConstNumber.NORMAL_NUMBER10,  ConstNumber.NORMAL_NUMBER10,  ConstNumber.NORMAL_NUMBER10);
        Vector bfl = GlobalFunc1.createVector(-ConstNumber.NORMAL_NUMBER10, -ConstNumber.NORMAL_NUMBER10, -ConstNumber.NORMAL_NUMBER10);
        Vector bfr = GlobalFunc1.createVector( ConstNumber.NORMAL_NUMBER10, -ConstNumber.NORMAL_NUMBER10, -ConstNumber.NORMAL_NUMBER10);
        Vector bbl = GlobalFunc1.createVector(-ConstNumber.NORMAL_NUMBER10, -ConstNumber.NORMAL_NUMBER10,  ConstNumber.NORMAL_NUMBER10);
        Vector bbr = GlobalFunc1.createVector( ConstNumber.NORMAL_NUMBER10, -ConstNumber.NORMAL_NUMBER10,  ConstNumber.NORMAL_NUMBER10);
        ArrayList<Triangle> triangles = new ArrayList<>(Arrays.asList(
                new Triangle(tfl, tfr, bfr),
                new Triangle(tfl, bfr, bfl),
                new Triangle(tbl, tbr, bbr),
                new Triangle(tbl, bbr, bbl),
                new Triangle(tbl, tfl, bbl),
                new Triangle(tfl, bfl, bbl),
                new Triangle(tbr, tfr, bbr),
                new Triangle(tfr, bfr, bbr),
                new Triangle(tbl, tbr, tfr),
                new Triangle(tbl, tfr, tfl),
                new Triangle(bbl, bbr, bfr),
                new Triangle(bbl, bfr, bfl)
        ));
        ShaderCallback floorShader = (tri, pos, view) -> {
            Vector green = GlobalFunc1.createVector(0.0, ConstNumber.NORMAL_FLOAT_0P4, 0.0);
            Vector grey = GlobalFunc1.createVector(ConstNumber.NORMAL_FLOAT_0P4, ConstNumber.NORMAL_FLOAT_0P4, ConstNumber.NORMAL_FLOAT_0P4);
            grey.reflection = 1.0;
            double x = ((pos.values[0] / ConstNumber.NORMAL_NUMBER32) % ConstNumber.NORMAL_NUMBER2 + ConstNumber.NORMAL_NUMBER2) % ConstNumber.NORMAL_NUMBER2;
            double z = ((pos.values[ConstNumber.ARRAY_INDEX2] / ConstNumber.NORMAL_NUMBER32 + ConstNumber.NORMAL_FLOAT_0P3) % ConstNumber.NORMAL_NUMBER2 + ConstNumber.NORMAL_NUMBER2) % ConstNumber.NORMAL_NUMBER2;
            if ((x < 1) != (z < 1)) {
                return grey;
            } else {
                return green;
            }
        };
        Vector ffl = GlobalFunc1.createVector(-ConstNumber.NORMAL_NUMBER1000, -ConstNumber.NORMAL_NUMBER30, -ConstNumber.NORMAL_NUMBER1000);
        Vector ffr = GlobalFunc1.createVector( ConstNumber.NORMAL_NUMBER1000, -ConstNumber.NORMAL_NUMBER30, -ConstNumber.NORMAL_NUMBER1000);
        Vector fbl = GlobalFunc1.createVector(-ConstNumber.NORMAL_NUMBER1000, -ConstNumber.NORMAL_NUMBER30,  ConstNumber.NORMAL_NUMBER1000);
        Vector fbr = GlobalFunc1.createVector( ConstNumber.NORMAL_NUMBER1000, -ConstNumber.NORMAL_NUMBER30,  ConstNumber.NORMAL_NUMBER1000);
        Triangle tr13 = new Triangle(fbl, fbr, ffr);
        tr13.shader = floorShader;
        triangles.add(tr13);
        Triangle tr14 = new Triangle(fbl, ffr, ffl);
        tr14.shader = floorShader;
        triangles.add(tr14);
        // 通过三角形数组构建场景
        Scene _scene = new Scene(triangles);
        // 设置场景灯光
        _scene.lights[0] = GlobalFunc1.createVector(ConstNumber.NORMAL_NUMBER20, ConstNumber.NORMAL_NUMBER38, -ConstNumber.NORMAL_NUMBER22);
        _scene.lights[0].colour = GlobalFunc1.createVector(ConstNumber.NORMAL_FLOAT_0P7, ConstNumber.NORMAL_FLOAT_0P3, ConstNumber.NORMAL_FLOAT_0P3);
        _scene.lights[1] = GlobalFunc1.createVector(-ConstNumber.NORMAL_NUMBER23, ConstNumber.NORMAL_NUMBER40, ConstNumber.NORMAL_NUMBER17);
        _scene.lights[1].colour = GlobalFunc1.createVector(ConstNumber.NORMAL_FLOAT_0P7, ConstNumber.NORMAL_FLOAT_0P3, ConstNumber.NORMAL_FLOAT_0P3);
        _scene.lights[ConstNumber.ARRAY_INDEX2] = GlobalFunc1.createVector(ConstNumber.NORMAL_NUMBER23, ConstNumber.NORMAL_NUMBER20, ConstNumber.NORMAL_NUMBER17);
        _scene.lights[ConstNumber.ARRAY_INDEX2].colour = GlobalFunc1.createVector(ConstNumber.NORMAL_FLOAT_0P7, ConstNumber.NORMAL_FLOAT_0P7, ConstNumber.NORMAL_FLOAT_0P7);
        // 设置背景
        _scene.ambient = GlobalFunc1.createVector(ConstNumber.NORMAL_FLOAT_0P1, ConstNumber.NORMAL_FLOAT_0P1, ConstNumber.NORMAL_FLOAT_0P1);
        int size = ConstNumber.NORMAL_NUMBER30;
        // 初始化像素数组
        Vector[][] pixels = new Vector[size][];
        for (int y = 0; y < size; y++) {
            pixels[y] = new Vector[size];
            for (int x = 0; x < size; x++) {
                pixels[y][x] = Vector.zero();
            }
        }
        // 初始化相机
        Camera _camera = new Camera(GlobalFunc1.createVector(-ConstNumber.NORMAL_NUMBER40, ConstNumber.NORMAL_NUMBER40, ConstNumber.NORMAL_NUMBER40), GlobalFunc1.createVector(0, 0, 0), GlobalFunc1.createVector(0, 1, 0));
        // 执行3d-raytrace算法，生成像素数组
        _camera.render(_scene, pixels, size, size);
        return pixels;
    }

}

class ThreeDRaytrace {
    public static String arrayToCanvasCommands(Vector[][] pixels) {
        String s = "<canvas id=\"renderCanvas\" width=\"30px\" height=\"30px\"></canvas><scr" + "ipt>\nvar pixels = [";
        int size = ConstNumber.NORMAL_NUMBER30;
        for (int y = 0; y < size; y++) {
            s += "[";
            for (int x = 0; x < size; x++) {
                s += "[" + pixels[y][x].scriptString() + "],";
            }
            s += "],";
        }
        s += """
                ];\n    var canvas = document.getElementById("renderCanvas").getContext("2d");\n\
                \n\
                \n\
                    var size = 30;\n\
                    canvas.fillStyle = "red";\n\
                    canvas.fillRect(0, 0, size, size);\n\
                    canvas.scale(1, -1);\n\
                    canvas.translate(0, -size);\n\
                \n\
                    if (!canvas.setFillColor)\n\
                        canvas.setFillColor = function(r, g, b, a) {\n\
                            this.fillStyle = "rgb("+[Math.floor(r * 255), Math.floor(g * 255), Math.floor(b * 255)]+")";\n}\n\
                \n\
                for (var y = 0; y < size; y++) {\n\
                  for (var x = 0; x < size; x++) {\n\
                    var l = pixels[y][x];\n\
                    canvas.setFillColor(l[0], l[1], l[2], 1);\n\
                    canvas.fillRect(x, y, 1, 1);\n\
                  }\n\
                }</scr""" + "ipt>";
           return s;
    }
}

class Configuration {
    public static final boolean DEBUG = false;
    public static final boolean showRenderJs = false;
}

class Benchmark {

    public static int runCount = ConstNumber.ITERATION_RUNCOUNT;

    public static void main(String[] args) {
        Benchmark.run();
    }

    /*
     * @Benchmark
     */
    public static void run() {
        long startTime = (long) (System.nanoTime());
        for (int i = 0; i < runCount; i++) {
            String result = ThreeDRaytrace.arrayToCanvasCommands(GlobalFunc2.raytraceScene());
            if (Configuration.DEBUG && Configuration.showRenderJs) {

                System.out.println(result);
            }
        }

        long endTime = (long) (System.nanoTime());
        System.out.println("3d-raytrace: ms = " + (double)(endTime - startTime) / 1000000);
    }
}